package com.easyaop.test.utils;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.util.ASMifier;
import org.objectweb.asm.util.CheckClassAdapter;
import org.objectweb.asm.util.Textifier;
import org.objectweb.asm.util.TraceClassVisitor;

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Base64;

/**
 * @author lipan
 * @since 2024-09-16
 */
public class ASMUtils {
    public static final int SKIP_CODE = ClassReader.SKIP_CODE,
            SKIP_DEBUG = ClassReader.SKIP_DEBUG,
            SKIP_FRAMES = ClassReader.SKIP_FRAMES,
            EXPAND_FRAMES = ClassReader.EXPAND_FRAMES;

    public static byte[] read(Class<?> clazz) {
        try {
            ClassReader cr = new ClassReader(clazz.getName());
            ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
            cr.accept(cw, 0);
            return cw.toByteArray();
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
    }

    public static byte[] readBase64(String str) {
        final byte[] decode = Base64.getDecoder().decode(str);
        ClassReader cr = new ClassReader(decode);
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
        TraceClassVisitor tcv = new TraceClassVisitor(cw, new PrintWriter(System.out));
        cr.accept(tcv, 0);
        return cw.toByteArray();
    }

    public static byte[] checkBase64(String str) {
        final byte[] decode = Base64.getDecoder().decode(str);
        ClassReader cr = new ClassReader(decode);
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
        CheckClassAdapter cca = new CheckClassAdapter(cw);
        TraceClassVisitor tcv = new TraceClassVisitor(cca, new PrintWriter(System.out));
        cr.accept(tcv, 0);
        return cw.toByteArray();
    }

    public static byte[] read(String allName) {
        try {
            ClassReader cr = new ClassReader(allName);
            ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
            cr.accept(cw, 0);
            return cw.toByteArray();
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
    }

    public static byte[] print(byte[] data) {
        ClassReader cr = new ClassReader(data);
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
        TraceClassVisitor tcv = new TraceClassVisitor(cw, new Textifier(), new PrintWriter(System.out));
        cr.accept(tcv, 0);
        return cw.toByteArray();
    }

    public static byte[] print(InputStream inputStream) {
        try {
            ClassReader cr = new ClassReader(inputStream);
            ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
            TraceClassVisitor tcv = new TraceClassVisitor(cw, new Textifier(), new PrintWriter(System.out));
            cr.accept(tcv, 0);
            return cw.toByteArray();
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
    }

    public static byte[] checkPrint(byte[] data) {
        ClassReader cr = new ClassReader(data);
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
        CheckClassAdapter cca = new CheckClassAdapter(cw);
        TraceClassVisitor tcv = new TraceClassVisitor(cca, new Textifier(), new PrintWriter(System.out));
        cr.accept(tcv, 0);
        return cw.toByteArray();
    }

    public static byte[] print(Class<?> clazz) {
        try {
            ClassReader cr = new ClassReader(clazz.getName());
            ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
            TraceClassVisitor tcv = new TraceClassVisitor(cw, new Textifier(), new PrintWriter(System.out));
            cr.accept(tcv, 0);
            return cw.toByteArray();
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
    }

    public static byte[] printASM(Class<?> clazz) {
        return printASM(clazz, 0);
    }

    public static byte[] printASM(Class<?> clazz, int flags) {
        try {
            ClassReader cr = new ClassReader(clazz.getName());
            ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
            TraceClassVisitor tcv = new TraceClassVisitor(cw, new ASMifier(), new PrintWriter(System.out));
            cr.accept(tcv, flags);
            return cw.toByteArray();
        } catch (IOException ex) {
            throw new IllegalArgumentException(ex);
        }
    }
}
